package net.happy2v.file.server.test.file;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.UUID;

import net.ib.mn.protocol.Common.UUIDMsg;

import com.google.common.primitives.Longs;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;

/**
 * UUID, UUIDMsg瑜��ъ슜���� �꾩슂���좎슜���좏떥由ы떚 �대옒�� * 
 * @author 0mok@infobank.net
 * 
 */
public final class UUIDs {

	private final static UUIDMsgComparator uuidMsgComparator = new UUIDMsgComparator();
	private final static UUIDComparator uuidComparator = new UUIDComparator();

	private UUIDs() {
	}

	public static UUIDMsg convert(UUID uuid) {
		if (uuid == null)
			return null;
		return UUIDMsg
				.newBuilder()
				.setMsb(ByteString.copyFrom(Longs.toByteArray(uuid
						.getMostSignificantBits())))
				.setLsb(ByteString.copyFrom(Longs.toByteArray(uuid
						.getLeastSignificantBits()))).build();
	}

	public static UUID convert(UUIDMsg uuidMsg) {
		if (uuidMsg == null)
			return null;
		return new UUID(Longs.fromByteArray(uuidMsg.getMsb().toByteArray()),
				Longs.fromByteArray(uuidMsg.getLsb().toByteArray()));
	}

	
	/**
	 * UUID v.1
	 * 
	 * @return
	 */
	public static UUID createTimeUUID() {
		com.eaio.uuid.UUID uuid = new com.eaio.uuid.UUID();
		return new UUID(uuid.time, uuid.clockSeqAndNode);
	}

	/**
	 * time_low(4bytes), time_mid(2bytes), time_hi(2bytes) �쒖꽌瑜���쑝濡�蹂�꼍�섏뿬 HBase
	 * byte order媛�媛�뒫�섍쾶 ��	 * 
	 * @return
	 */
	public static UUID switchTimeUUID(UUID uuid) {

		long mostSignificantBits = uuid.getMostSignificantBits();
		long time = 0;
		
        time =  (mostSignificantBits << 48) & 0xffff000000000000L;
        time |= (mostSignificantBits >> 32) & 0x00000000ffffffffL;
        time |= (mostSignificantBits << 16) & 0x0000ffff00000000L;

		return new UUID(time, uuid.getLeastSignificantBits());
	}

	public static UUID restoreOriginalTimeUUID(UUID uuid) {

		long mostSignificantBits = uuid.getMostSignificantBits();
		long time = 0;
		
        time =  (mostSignificantBits >> 48) & 0x000000000000ffffL;
        time |= (mostSignificantBits << 32) & 0xffffffff00000000L;
        time |= (mostSignificantBits >> 16) & 0x00000000ffff0000L; 
		
		return new UUID(time, uuid.getLeastSignificantBits());
	}

	public static UUID switchAndReverseTimeUUID(UUID uuid) {
		long mostSignificantBits = uuid.getMostSignificantBits();
		long time = 0;
		
        time =  (mostSignificantBits << 48) & 0xffff000000000000L;
        time |= (mostSignificantBits >> 32) & 0x00000000ffffffffL;
        time |= (mostSignificantBits << 16) & 0x0000ffff00000000L;

		return new UUID(time ^ MAX_ULONG, uuid.getLeastSignificantBits() ^ MAX_ULONG);
	}
	
	public static UUIDMsg switchAndReverseTimeUUID(UUIDMsg uuidMsg) {
		long mostSignificantBits = Longs.fromByteArray(uuidMsg.getMsb().toByteArray());
		long leastSignificantBits = Longs.fromByteArray(uuidMsg.getLsb().toByteArray());
		long time = 0;
		
        time =  (mostSignificantBits << 48) & 0xffff000000000000L;
        time |= (mostSignificantBits >> 32) & 0x00000000ffffffffL;
        time |= (mostSignificantBits << 16) & 0x0000ffff00000000L;

		return UUIDMsg.newBuilder()
				.setMsb(ByteString.copyFrom(Longs.toByteArray(time ^ MAX_ULONG)))
				.setLsb(ByteString.copyFrom(Longs.toByteArray(leastSignificantBits ^ MAX_ULONG))).build();
	}
	/**
	 * UUID v.3
	 * 
	 * @param name
	 * @return
	 */
	public static UUID createNameUUID(byte[] name) {
		return UUID.nameUUIDFromBytes(name);
	}

	/**
	 * UUID v.4
	 * 
	 * @return
	 */
	public static UUID createRandomUUID() {
		return UUID.randomUUID();
	}

	public static Comparator<UUIDMsg> getUUIDMsgComparator() {
		return uuidMsgComparator;
	}

	/**
	 * TimeUUID瑜�鍮꾧탳�����ъ슜�����덈뒗 {@link UUID} {@link Comparator} <br>
	 * Java�먯꽌 湲곕낯�쇰줈 �쒓났�섎뒗 {@link UUID}��compareTo硫붿꽌�쒓� MSB, LSB 鍮꾧탳瑜�Signed Long湲곗��쇰줈
	 * 鍮꾧탳�섍린 �뚮Ц��TimeUUID���곹빀�섏� �딅떎.
	 * 
	 * @return TimeUUID 鍮꾧탳���ъ슜�����덈뒗 Comparator
	 */
	public static Comparator<UUID> getUUIDComparator() {
		return uuidComparator;
	}

	public static int compare(UUIDMsg x, UUIDMsg y) {
		return compareTo(x.toByteArray(), y.toByteArray());

	}

	private static final class UUIDMsgComparator implements Comparator<UUIDMsg> {

		@Override
		public int compare(UUIDMsg o1, UUIDMsg o2) {
			return UUIDs.compare(o1, o2);
		}
	}

	public static int compare(UUID x, UUID y) {
		return (isLessThanUnsigned(x.getMostSignificantBits(),
				y.getMostSignificantBits()) ? -1 : (isGreaterThanUnsigned(
				x.getMostSignificantBits(), y.getMostSignificantBits()) ? 1
				: (isLessThanUnsigned(x.getLeastSignificantBits(),
						y.getLeastSignificantBits()) ? -1
						: (isGreaterThanUnsigned(x.getLeastSignificantBits(),
								y.getLeastSignificantBits()) ? 1 : 0))));
	}

	private static final class UUIDComparator implements Comparator<UUID> {
		@Override
		public int compare(UUID o1, UUID o2) {
			return UUIDs.compare(o1, o2);
		}
	}

	private static boolean isLessThanUnsigned(long n1, long n2) {
		return (n1 < n2) ^ ((n1 < 0) != (n2 < 0));
	}

	private static boolean isGreaterThanUnsigned(long n1, long n2) {
		return (n1 > n2) ^ ((n1 < 0) != (n2 < 0));
	}

	private static final long MAX_ULONG = 0xFFFFFFFFFFFFFFFFL;

	public static final UUIDMsg reverse(UUIDMsg uuidMsg) {
		return UUIDMsg
				.newBuilder()
				.setMsb(ByteString.copyFrom(reverse(uuidMsg.getMsb()
						.toByteArray())))
				.setLsb(ByteString.copyFrom(reverse(uuidMsg.getLsb()
						.toByteArray()))).build();
	}

	private static final byte[] reverse(byte[] original) {
		byte[] reverse = new byte[original.length];
		for (int i = 0; i < original.length; i++) {
			reverse[i] = (byte) (original[i] ^ 0xFF);
		}
		return reverse;
	}

	public static final UUID reverse(UUID uuid) {
		return new UUID(uuid.getMostSignificantBits() ^ MAX_ULONG,
				uuid.getLeastSignificantBits() ^ MAX_ULONG);
	}

	public static final UUID MAX_UUID = new UUID(MAX_ULONG, MAX_ULONG);
	public static final UUID MIN_UUID = new UUID(0, 0);
	public static final byte[] MAX_ULONG_BYTES = Longs.toByteArray(MAX_ULONG);
	public static final byte[] MIN_ULONG_BYTES = Longs.toByteArray(0);

	public static final UUIDMsg MAX_UUIDMsg = UUIDMsg.newBuilder()
			.setMsb(ByteString.copyFrom(MAX_ULONG_BYTES))
			.setLsb(ByteString.copyFrom(MAX_ULONG_BYTES)).build();
	public static final UUIDMsg MIN_UUIDMsg = UUIDMsg.newBuilder()
			.setMsb(ByteString.copyFrom(MIN_ULONG_BYTES))
			.setLsb(ByteString.copyFrom(MIN_ULONG_BYTES)).build();

	/**
	 * @param left
	 *            left operand
	 * @param right
	 *            right operand
	 * @return 0 if equal, < 0 if left is less than right, etc.
	 */
	private static int compareTo(final byte[] left, final byte[] right) {
		return compareTo(left, 0, left.length, right, 0, right.length);
	}

	/**
	 * Lexographically compare two arrays.
	 * 
	 * @param buffer1
	 *            left operand
	 * @param buffer2
	 *            right operand
	 * @param offset1
	 *            Where to start comparing in the left buffer
	 * @param offset2
	 *            Where to start comparing in the right buffer
	 * @param length1
	 *            How much to compare from the left buffer
	 * @param length2
	 *            How much to compare from the right buffer
	 * @return 0 if equal, < 0 if left is less than right, etc.
	 */
	private static int compareTo(byte[] buffer1, int offset1, int length1,
			byte[] buffer2, int offset2, int length2) {
		// Bring WritableComparator code local
		int end1 = offset1 + length1;
		int end2 = offset2 + length2;
		for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) {
			int a = (buffer1[i] & 0xff);
			int b = (buffer2[j] & 0xff);
			if (a != b) {
				return a - b;
			}
		}
		return length1 - length2;
	}
	
	public static String convertTime(UUID uuid, String pattern) {

		if (uuid.version() == 1) {
			Calendar cal = GregorianCalendar.getInstance();
			long timestamp = uuid.timestamp();
			cal.setTimeInMillis((timestamp / 10000) -12219292800000L);
			
	    	SimpleDateFormat df = new SimpleDateFormat(pattern);
	    	String strDate = df.format(cal.getTime());
			
			return strDate;
		} else {
			return null;
		}

	}
	
	public static List<UUIDMsg> convert(List<UUID> uuidList) {
		List<UUIDMsg> uuidMsgList = new ArrayList<UUIDMsg>();
		for (UUID uuid : uuidList) {
			uuidMsgList.add(convert(uuid));
		}
		return uuidMsgList;
	}
	
	public static List<UUIDMsg> convertMsgList(List<String> stringList) {
		List<UUIDMsg> uuidMsgList = new ArrayList<UUIDMsg>();
		for (String uuid : stringList) {
			uuidMsgList.add(convert(UUID.fromString(uuid)));
		}
		return uuidMsgList;
	}
	
	public static UUID getLsbMinusOne(UUID uuid) {
		return new UUID(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()-1);
	}
	
	public static void main(String[] args) throws InterruptedException,
			InvalidProtocolBufferException {

		int i = 0;
		while (i++ < 10) {
			UUID time = createTimeUUID();
			System.out.println(convertTime(time, "yyyy-MM-dd HH:mm:ss"));
		}
	}
}
